package com.atguigu.tingshu.search.service.impl;
import cn.hutool.core.util.PageUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.*;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.result.Result;

import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexSearchVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.search.service.SearchService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;




@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class SearchServiceImpl implements SearchService {


    @Autowired
    private AlbumIndexRepository albumIndexRepository;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private CategoryFeignClient categoryFeignClient;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void upperAlbum(Long albumId) {
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();


        CompletableFuture<AlbumInfo> cf1 = CompletableFuture.supplyAsync(() -> {
            Result<AlbumInfo> infoResult = albumInfoFeignClient.getAlbumInfoById(albumId);
            AlbumInfo albumInfo = infoResult.getData();
            BeanUtils.copyProperties(albumInfo, albumInfoIndex);
            albumInfoIndex.setIsFinished(String.valueOf(albumInfo.getIsFinished()));

            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            List<AttributeValueIndex> attributeValueIndexList = albumAttributeValueVoList.stream().map(albumAttributeValue -> {
                AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                attributeValueIndex.setValueId(albumAttributeValue.getValueId());
                return attributeValueIndex;
            }).collect(Collectors.toList());
            albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            return albumInfo;
        });


        CompletableFuture<Void> cf2 = cf1.thenAcceptAsync(albumInfo -> {
            Result<BaseCategoryView> viewResult = categoryFeignClient.findCategoryByC3Id(albumInfo.getCategory3Id());
            BaseCategoryView baseCategoryView = viewResult.getData();
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
        });


        CompletableFuture<Void> cf3 = cf1.thenAcceptAsync(albumInfo -> {
            Result<UserInfo> userInfoResult = userInfoFeignClient.findByUserId(albumInfo.getUserId());
            UserInfo userInfo = userInfoResult.getData();
            albumInfoIndex.setAnnouncerName(userInfo.getNickname());
        });


        CompletableFuture<Void> cf4 = CompletableFuture.runAsync(() -> {
            Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.findAlbumStatByAlbumId(albumId);
            AlbumStatVo albumStatVo = albumStatVoResult.getData();
            Integer playStatNum = albumStatVo.getPlayStatNum();
            albumInfoIndex.setPlayStatNum(playStatNum);
            Integer subscribeStatNum = albumStatVo.getSubscribeStatNum();
            albumInfoIndex.setSubscribeStatNum(subscribeStatNum);
            Integer buyStatNum = albumStatVo.getBuyStatNum();
            albumInfoIndex.setBuyStatNum(buyStatNum);
            Integer commentStatNum = albumStatVo.getCommentStatNum();
            albumInfoIndex.setCommentStatNum(commentStatNum);
            albumInfoIndex.setHotScore(playStatNum * 0.7 + subscribeStatNum * 0.1 + buyStatNum * 0.1 + commentStatNum * 0.1);

        });


        CompletableFuture.allOf(cf1, cf2, cf3, cf4).join();
        albumIndexRepository.save(albumInfoIndex);
        log.info(JSON.toJSONString(albumInfoIndex));

        // 初始化提词库
        CompletableFuture.runAsync(() -> {
            initSuggestIndex(albumInfoIndex) ;
        });

    }

    // 初始化提词库
    private void initSuggestIndex(AlbumInfoIndex albumInfoIndex) {
        String albumTitle = albumInfoIndex.getAlbumTitle() ;
        String albumIntro = albumInfoIndex.getAlbumIntro() ;
        String albumAnnouncerName = albumInfoIndex.getAnnouncerName() ;

        // 创建专辑标题索引数据
        SuggestIndex albumTitleSuggestIndex = new SuggestIndex() ;
        albumTitleSuggestIndex.setId(UUID.randomUUID().toString().replace("-" , ""));
        albumTitleSuggestIndex.setTitle(albumTitle);
        albumTitleSuggestIndex.setKeyword(new Completion(new String[]{albumTitle}));
        albumTitleSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumTitle)}));
        albumTitleSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumTitle)}));
        suggestIndexRepository.save(albumTitleSuggestIndex) ;

        //  专辑简介提词
        SuggestIndex albumIntroSuggestIndex = new SuggestIndex();
        albumIntroSuggestIndex.setId(UUID.randomUUID().toString().replaceAll("-",""));
        albumIntroSuggestIndex.setTitle(albumIntro);
        albumIntroSuggestIndex.setKeyword(new Completion(new String[]{albumIntro}));
        albumIntroSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumIntro)}));
        albumIntroSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumIntro)}));
        this.suggestIndexRepository.save(albumIntroSuggestIndex);

        // 专辑主播提词
        SuggestIndex announcerSuggestIndex = new SuggestIndex();
        announcerSuggestIndex.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        announcerSuggestIndex.setTitle(albumAnnouncerName);
        announcerSuggestIndex.setKeyword(new Completion(new String[]{albumAnnouncerName}));
        announcerSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumAnnouncerName)}));
        announcerSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumAnnouncerName)}));
        suggestIndexRepository.save(announcerSuggestIndex);

    }

    @Override
    public void lowerAlbum(Long albumId) {
        albumIndexRepository.deleteById(albumId);
    }

    @Override
    @SneakyThrows
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {

        SearchRequest searchRequest = buildSearchRequest(albumIndexQuery);

        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        AlbumSearchResponseVo albumSearchResponseVo = parseSearchResponse(searchResponse,albumIndexQuery);

        return albumSearchResponseVo;
    }

    private SearchRequest buildSearchRequest(AlbumIndexQuery albumIndexQuery) {
        // 创建SearchRequest对象
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();

        // 搜索检索条件对象
        BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();

        // 设置关键字搜索查询条件
        String keyword = albumIndexQuery.getKeyword();
        if(!StringUtils.isEmpty(keyword)) {
            boolQueryBuilder.must(QueryBuilders.multiMatch(builder -> builder.fields("albumTitle" , "albumIntro").query(keyword))) ;
        }

        // 设置分类搜索条件
        Long category1Id = albumIndexQuery.getCategory1Id();
        if(category1Id != null) {
            boolQueryBuilder.filter(QueryBuilders.term(builder -> builder.field("category1Id").value(category1Id))) ;
        }

        Long category2Id = albumIndexQuery.getCategory2Id();
        if(category2Id != null) {
            boolQueryBuilder.filter(QueryBuilders.term(builder -> builder.field("category2Id").value(category2Id))) ;
        }

        Long category3Id = albumIndexQuery.getCategory3Id();
        if(category3Id != null) {
            boolQueryBuilder.filter(QueryBuilders.term(builder -> builder.field("category3Id").value(category3Id))) ;
        }

        // 根据属性Id检索 请求参数格式：属性Id:属性值Id
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if(!CollectionUtils.isEmpty(attributeList)) {
            for(String attributeIdAndValuId : attributeList) {
                String[] attributeInfoArr = attributeIdAndValuId.split(":");
                if(attributeInfoArr != null && attributeInfoArr.length == 2) {      // 增强代码的健壮性

                    // 构建多条件组件查询对象，封装属性id和属性值id的搜索条件
                    BoolQuery boolQuery = QueryBuilders.bool()
                            .must(QueryBuilders.term(attributeIdBuilder -> attributeIdBuilder.field("attributeValueIndexList.attributeId").value(Long.parseLong(attributeInfoArr[0]))))
                            .must(QueryBuilders.term(attrValueBuilder -> attrValueBuilder.field("attributeValueIndexList.valueId").value(Long.parseLong(attributeInfoArr[1]))))
                            .build();

                    // 构建nested查询对象，设置nested类型参数
                    Query nestedQuery = QueryBuilders.nested(builder -> builder.path("attributeValueIndexList").query(boolQuery._toQuery()));
                    boolQueryBuilder.filter(nestedQuery) ;
                }
            }
        }
        Query query = new Query.Builder().bool(boolQueryBuilder.build()).build();

        // 将检索条件对象设置给SearchRequestBuilder对象
        searchRequestBuilder.index("albuminfo").query(query) ;

        // 设置分页参数
        Integer from = (albumIndexQuery.getPageNo() - 1) * albumIndexQuery.getPageSize() ;
        Integer size = albumIndexQuery.getPageSize() ;
        searchRequestBuilder.from(from) ;
        searchRequestBuilder.size(size) ;

        // 设置排序参数, 参数格式（综合排序[1:desc] 播放量[2:desc] 发布时间[3:desc]；asc:升序 desc:降序）
        String orderInfo = albumIndexQuery.getOrder();
        FieldSort fieldSort = new FieldSort.Builder().field("hotScore").order(SortOrder.Desc).build() ;     // 设置默认的排序规则
        if(!StringUtils.isEmpty(orderInfo)) {
            String[] orderInfoArr = orderInfo.split(":");
            if(orderInfoArr != null && orderInfoArr.length == 2) {
                SortOrder sortOrder = "asc".equalsIgnoreCase(orderInfoArr[1])? SortOrder.Asc : SortOrder.Desc ;
                switch (orderInfoArr[0]) {
                    case "1":
                        fieldSort = new FieldSort.Builder().field("hotScore").order(sortOrder).build() ;
                        break ;
                    case "2":
                        fieldSort = new FieldSort.Builder().field("playStatNum").order(sortOrder).build() ;
                        break;
                    case "3":
                        fieldSort = new FieldSort.Builder().field("createTime").order(sortOrder).build() ;
                        break;
                }
            }
        }
        SortOptions sortOptions = new SortOptions.Builder().field(fieldSort).build();
        searchRequestBuilder.sort(sortOptions) ;

        // 设置高亮参数
        if(!StringUtils.isEmpty(keyword)) {
            Map<String , HighlightField> highlightFieldMap = new HashMap<>() ;
            highlightFieldMap.put("albumTitle" , HighlightField.of(highlightFieldBuilder -> highlightFieldBuilder.preTags("<font color='red'>").postTags("</font>"))) ;
            highlightFieldMap.put("albumIntro" , HighlightField.of(highlightFieldBuilder -> highlightFieldBuilder.preTags("<font color='red'>").postTags("</font>"))) ;
            Highlight highlight = new Highlight.Builder().fields(highlightFieldMap).build();
            searchRequestBuilder.highlight(highlight) ;
        }

        // 构建SearchRequest对象
        SearchRequest searchRequest = searchRequestBuilder.build();

        log.info("dsl语句: {}" , searchRequest.toString());

        // 返回
        return  searchRequest;
    }

    private AlbumSearchResponseVo parseSearchResponse(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery albumIndexQuery) {

        // 创建AlbumSearchResponseVo对象，封装响应结果数据
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo() ;

        // 获取搜索结果
        HitsMetadata<AlbumInfoIndex> hitsMetadata = searchResponse.hits();
        List<Hit<AlbumInfoIndex>> hits = hitsMetadata.hits();
        if(!CollectionUtils.isEmpty(hits)) {

            // 获取结果数据，并将其转换为AlbumInfoIndexVo对象
            List<AlbumInfoIndexVo> albumInfoIndexVoList = hits.stream().map(hit -> {

                // 获取原始文档数据，构建目标对象
                AlbumInfoIndex albumInfoIndex = hit.source();
                AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
                BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);

                // 获取高亮字段
                Map<String, List<String>> highlightMap = hit.highlight();
                if (highlightMap != null && highlightMap.size() > 0) {

                    // 获取专辑标题高亮字段
                    List<String> albumTitleList = highlightMap.get("albumTitle");
                    if (!CollectionUtils.isEmpty(albumTitleList)) {
                        albumInfoIndexVo.setAlbumTitle(albumTitleList.get(0));
                    }

                    // 获取专辑简介高亮字段
                    List<String> albumIntroList = highlightMap.get("albumIntro");
                    if (!CollectionUtils.isEmpty(albumIntroList)) {
                        albumInfoIndexVo.setAlbumIntro(albumIntroList.get(0));
                    }
                }

                // 返回
                return albumInfoIndexVo;

            }).collect(Collectors.toList());

            // 设置结果数据
            albumSearchResponseVo.setList(albumInfoIndexVoList);
        }

        // 解析响应结果数据并将其封装到AlbumSearchResponseVo对象中
        Long totalNumber = hitsMetadata.total().value();        // 总记录数
        albumSearchResponseVo.setTotal(totalNumber);
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        Integer totalPage = PageUtil.totalPage(totalNumber.intValue(), albumIndexQuery.getPageSize());		// hutool工具类
        albumSearchResponseVo.setTotalPages(totalPage.longValue());

        // 返回
        return albumSearchResponseVo ;
    }

    @Override
    @SneakyThrows
    public List<AlbumInfoIndexSearchVo> channel(Long category1Id) {


        // search微服务远程调用album微服务获取根据一级分类的id获取置顶的三级分类数据
        Result<List<BaseCategory3>> topBaseCategory3 = categoryFeignClient.findTopBaseCategory3(category1Id);
        List<BaseCategory3> category3List = topBaseCategory3.getData();
        Map<Long, BaseCategory3> category3Map = category3List.stream().collect(Collectors.toMap((c3 -> c3.getId()), (c3 -> c3)));

        // 创建BoolQuery对象
        List<FieldValue> valueList = category3Map.keySet().stream().map(id -> FieldValue.of(id)).collect(Collectors.toList());
        BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();
        boolQueryBuilder.must(QueryBuilders.term(category1IdBuilder -> category1IdBuilder.field("category1Id").value(category1Id))) ;
        boolQueryBuilder.must(QueryBuilders.terms(category3Builder -> category3Builder.field("category3Id")
                .terms(new TermsQueryField.Builder().value(valueList).build()))) ;

        // 创建Query对象
        Query query = new Query.Builder().bool(boolQueryBuilder.build()).build();

        // 构建聚合对象
        // 创建排序对象
        FieldSort fieldSort = new FieldSort.Builder().field("hotScore").order(SortOrder.Desc).build() ;
        SortOptions sortOptions = new SortOptions.Builder().field(fieldSort).build();

        // 创建topHits聚合对象
        Aggregation topAlbumInfoAggregation = new Aggregation.Builder()
                .topHits(AggregationBuilders.topHits().size(6).sort(sortOptions).build())
                .build();

        // 创建三级分类id的自聚合对象
        Aggregation category3IdAggregation = new Aggregation.Builder()
                .terms(AggregationBuilders.terms().field("category3Id").size(100).build())
                .aggregations("topAlbumInfoAgg" , topAlbumInfoAggregation)
                .build();

        // 构建SearchRequest对象
        SourceConfig sourceConfig = new SourceConfig.Builder().fetch(false).build();
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        searchRequestBuilder.index("albuminfo")
                .query(query)
                .source(sourceConfig)
                .aggregations("category3IdAgg" , category3IdAggregation) ;

        // 得到SearchRequest对象
        SearchRequest searchRequest = searchRequestBuilder.build();
        log.info("dsl语句: {}" , searchRequest.toString());

        // 发送搜索请求
        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        // 解析响应结果
        Map<String, Aggregate> aggregations = searchResponse.aggregations();
        Aggregate aggregate = aggregations.get("category3IdAgg");
        LongTermsAggregate category3IdAggregate = (LongTermsAggregate)aggregate._get() ;
        Buckets<LongTermsBucket> longTermsBucketBuckets = category3IdAggregate.buckets();
        List<AlbumInfoIndexSearchVo> infoIndexSearchVoList = longTermsBucketBuckets.array().stream().map(bucket -> {

            long category3Id = bucket.key();     // 获取三级分类id

            // 获取前6条搜索到的原始文档数据
            Map<String, Aggregate> subAggregateMap = bucket.aggregations();
            Aggregate topAlbumInfoAggregate = subAggregateMap.get("topAlbumInfoAgg");
            TopHitsAggregate topHitsAggregate = (TopHitsAggregate) topAlbumInfoAggregate._get() ;
            HitsMetadata<JsonData> hitsMetadata = topHitsAggregate.hits();
            List<Hit<JsonData>> hits = hitsMetadata.hits();
            List<AlbumInfoIndex> infoIndexList = hits.stream().map(hit -> {
                String source = hit.source().toString();
                AlbumInfoIndex albumInfoIndex = JSON.parseObject(source, AlbumInfoIndex.class);
                return albumInfoIndex;
            }).collect(Collectors.toList());

            // 封装数据
            AlbumInfoIndexSearchVo infoIndexSearchVo = new AlbumInfoIndexSearchVo();
            BaseCategory3 baseCategory3 = category3Map.get(category3Id);
            infoIndexSearchVo.setBaseCategory3(baseCategory3);
            infoIndexSearchVo.setList(infoIndexList);

            // 返回封装结果数据
            return infoIndexSearchVo;

        }).collect(Collectors.toList());

        // 返回数据
        return infoIndexSearchVoList ;
    }

    @Override
    public List<String> completeSuggest(String keyword) {

        try {

            // 创建提示搜索对象
            FieldSuggester keyWordFieldSuggester = new FieldSuggester.Builder().prefix(keyword)
                    .completion(new CompletionSuggester.Builder()
                            .field("keyword")
                            .skipDuplicates(true)
                            .size(10)
                            .fuzzy(new SuggestFuzziness.Builder().fuzziness("auto").build())
                            .build()).build() ;

            FieldSuggester keywordPinyinSuggester = new FieldSuggester.Builder().prefix(keyword)
                    .completion(new CompletionSuggester.Builder()
                            .field("keywordPinyin")
                            .skipDuplicates(true)
                            .size(10)
                            .fuzzy(new SuggestFuzziness.Builder().fuzziness("auto").build())
                            .build()).build() ;

            FieldSuggester keywordSequenceSuggester = new FieldSuggester.Builder().prefix(keyword)
                    .completion(new CompletionSuggester.Builder()
                            .field("keywordSequence")
                            .skipDuplicates(true)
                            .size(10)
                            .fuzzy(new SuggestFuzziness.Builder().fuzziness("auto").build())
                            .build()).build() ;

            Suggester suggester = new Suggester.Builder()
                    .suggesters("suggestionKeyword", keyWordFieldSuggester)
                    .suggesters("suggestionkeywordPinyin" , keywordPinyinSuggester)
                    .suggesters("suggestionkeywordSequence" , keywordSequenceSuggester)
                    .build();


            // 构建SearchRequest对象
            SearchRequest searchRequest = new SearchRequest.Builder()
                    .index("suggestinfo")
                    .suggest(suggester)
                    .build() ;

            log.info("dsl语句：{}" , searchRequest.toString() );

            // 发送搜索请求
            SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(searchRequest, SuggestIndex.class);

            // 解析结果
            List<String> suggestResult = parseSuggestResult(searchResponse) ;

            // 返回
            return suggestResult ;

        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    private List<String> parseSuggestResult(SearchResponse<SuggestIndex> searchResponse) {
        // 创建List集合对象，封装响应结果数据
        List<String> suggestResult = new ArrayList<>() ;

        // 解析结果
        Map<String, List<Suggestion<SuggestIndex>>> suggestMap = searchResponse.suggest();

        // 获取keyword提示词结果
        suggestMap.get("suggestionKeyword").forEach(suggestion -> {
            CompletionSuggest<SuggestIndex> completion = suggestion.completion();
            completion.options().forEach(option -> suggestResult.add(option.source().getTitle()));
        });

        // 获取keywordPinyin提示词结果
        suggestMap.get("suggestionkeywordPinyin").forEach(suggestion -> {
            CompletionSuggest<SuggestIndex> completion = suggestion.completion();
            completion.options().forEach(option -> suggestResult.add(option.source().getTitle()));
        });

        // 获取keywordSequence提示词结果
        suggestMap.get("suggestionkeywordSequence").forEach(suggestion -> {
            CompletionSuggest<SuggestIndex> completion = suggestion.completion();
            completion.options().forEach(option -> suggestResult.add(option.source().getTitle()));
        });

        // 返回
        return suggestResult ;
    }

    @Override
    @SneakyThrows
    public void updateLatelyAlbumRanking() {

        // 创建一个Query对象
        Query query = new Query.Builder()
                .matchAll(QueryBuilders.matchAll().build())
                .build();

        // 创建SourceConfig对象
        SourceConfig sourceConfig = new SourceConfig.Builder()
                .fetch(false)
                .build();

        // 创建一个Aggregation对象
        Aggregation.Builder.ContainerBuilder containerBuilder = new Aggregation.Builder().terms(builder -> builder.field("category1Id").size(100)) ;

        String[] albumRanking = {"hotScore" , "playStatNum" , "subscribeStatNum" , "buyStatNum" , "commentStatNum"};
        for(String ranking : albumRanking) {

            // 创建Aggregation对象
            SortOptions sortOptions = new SortOptions.Builder().field(builder -> builder.field(ranking).order(SortOrder.Desc)).build();
            Aggregation aggregation = new Aggregation.Builder()
                    .topHits(builder -> builder.size(10).sort(sortOptions)).build();
            containerBuilder.aggregations(ranking + "Agg" , aggregation) ;

        }

        // 创建SearchRequest对象
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("albuminfo")
                .query(query)
                .source(sourceConfig)
                .aggregations("categoryIdAgg" , containerBuilder.build())
                .build() ;

        // 发送搜索请求
        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        // 解析响应结果
        Map<String, Aggregate> aggregations = searchResponse.aggregations();
        Aggregate aggregate = aggregations.get("categoryIdAgg");
        LongTermsAggregate longTermsAggregate = (LongTermsAggregate) aggregate._get() ;
        Buckets<LongTermsBucket> buckets = longTermsAggregate.buckets();
        List<LongTermsBucket> bucketList = buckets.array();

        for(LongTermsBucket categoryBucket : bucketList) {

            long categoryId = categoryBucket.key();   // 一级分类的id
            Map<String, Aggregate> aggregateMap = categoryBucket.aggregations();        // 获取一级分类聚合的自聚合结果
            for(String ranking : albumRanking) {

                Aggregate buyStatNumAgg = aggregateMap.get(ranking + "Agg");
                TopHitsAggregate topHitsAggregate = (TopHitsAggregate) buyStatNumAgg._get() ;
                HitsMetadata<JsonData> hitsMetadata = topHitsAggregate.hits();
                List<Hit<JsonData>> hits = hitsMetadata.hits();
                List<AlbumInfoIndex> infoIndexList = hits.stream().map(hit -> {                 // 记录的是购买量较高的前10条数据
                    AlbumInfoIndex albumInfoIndex = hit.source().to(AlbumInfoIndex.class);
                    return albumInfoIndex;
                }).collect(Collectors.toList());

                /**
                 * 把统计查询到的结果数据存储到Redis中，应该使用Redis中的哪一种数据类型？
                 * string、list、set、hash、zset
                 *
                 * string存储数据的方式
                 * album:ranking:一级分类的id:hotScore   专辑的list集合所对应的json字符串
                 * album:ranking:一级分类的id:playStatNum   专辑的list集合所对应的json字符串
                 * album:ranking:一级分类的id:subscribeStatNum   专辑的list集合所对应的json字符串
                 * album:ranking:一级分类的id:buyStatNum   专辑的list集合所对应的json字符串
                 * album:ranking:一级分类的id:commentStatNum   专辑的list集合所对应的json字符串
                 *
                 * hash数据的存储方式:
                 * album:ranking:一级分类的id    hotScore  专辑的list集合所对应的json字符串    playStatNum  专辑的list集合所对应的json字符串   xxxxx
                 *
                 */
                String redisKey = RedisConstant.RANKING_KEY_PREFIX + categoryId ;
                redisTemplate.opsForHash().put(redisKey , ranking , JSON.toJSONString(infoIndexList));

            }

        }
    }

    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long category1Id, String dimension) {
        String redisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id ;
        String jsonList = (String) redisTemplate.opsForHash().get(redisKey, dimension);
        if(!StringUtils.isEmpty(jsonList)) {
            List<AlbumInfoIndex> albumInfoIndexList = JSON.parseArray(jsonList, AlbumInfoIndex.class);
            List<AlbumInfoIndexVo> albumInfoIndexVoList = albumInfoIndexList.stream().map(albumInfoIndex -> {
                AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
                BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
                return albumInfoIndexVo;
            }).collect(Collectors.toList());
            return albumInfoIndexVoList ;
        }
        return List.of();
    }
}
